// 7、实现computed函数，要求computed在依赖数据变更的时候逻辑的计算结果也是响应式的
// 注意这个computed函数属于Composition API，与options选项的computed并不是一个东西

import { log } from "./playground.js";

// ----->来自模块2的代码
const targetMap = new WeakMap(); // 全局映射：响应式对象--->属性依赖集合
function trigger(targetMap, target, key) {
  const depMap = targetMap.get(target);
  if (!depMap) return;
  const dep = depMap.get(key);
  if (dep) {
    dep.forEach((effect) => {
      effect();
    });
  }
}

// ----->来自模块4的代码
const handler = {
  get(target, key, receiver) {
    let res = Reflect.get(target, key, receiver);
    track(targetMap, target, key); // 注意：在这里收集追踪依赖！！
    return res;
  },
  set(target, key, value, receiver) {
    let previousValue = target[key]; // 旧值
    let res = Reflect.set(target, key, value, receiver); // 注意先后顺序，先反射再检测值是否需要触发更新
    if (previousValue !== value) {
      trigger(targetMap, target, key);
    }
    return res;
  },
};
function reactive(target) {
  return new Proxy(target, handler);
}

// ----->来自模块5的代码
let activeEffect = null; // 用来记录runtime的函数，Vue 2中也运用过这个标记技巧

function effect(eff) {
  activeEffect = eff;
  activeEffect();
  activeEffect = null;
}
function track(targetMap, target, key) {
  if (activeEffect) {
    let depMap = targetMap.get(target);
    if (!depMap) {
      depMap = new Map();
      targetMap.set(target, depMap);
    }
    let dep = depMap.get(key);
    if (!dep) {
      dep = new Set();
      depMap.set(key, dep);
    }
    dep.add(activeEffect); // 这里有改动！将正在运行的函数作为属性依赖加入dep队列中
  }
}

// ----->来自模块6的代码

function ref(primitiveValue) {
  const refObj = {
    get value() {
      track(targetMap, refObj, "value"); // 收集该对象的依赖
      return primitiveValue;
    },
    set value(newValue) {
      if (primitiveValue !== newValue) {
        primitiveValue = newValue;
        trigger(targetMap, refObj, "value"); // 触发该属性的依赖
      }
    },
  };
  return refObj;
}

// ----->下面开始的是模块7的代码

// computed函数，内核是基于ref类型实现，它的value值是响应式的，本身也是ref类型
function computed(computedLogic) {
  let result = ref(); // 创建一个ref类型
  effect(() => (result.value = computedLogic())); // 在effect中跑一边计算逻辑并收集依赖，相当于监听了因变量
  return result; // 返回计算结果。并且它的value是响应式的
}

// 测试用例 7
let stringObj = reactive({
  content: "hello Vue 2.0 !",
}); // 这个stringObj是响应式的
let reverseString = computed(() =>
  stringObj.content.split("").reverse().join("")
); // reverseString是一个ref类型，它的value值基于computed逻辑也是响应式的
let amazingString = computed(() => stringObj.content + "!!!!"); // 同reverseString
// 以下的combineString依赖于两个计算逻辑reverseString和amazingString
let combineString = computed(() => amazingString.value + reverseString.value);
log(reverseString.value, amazingString.value, 1);
log(combineString.value, 2);
stringObj.content = "hello Vue 3.0 !";
log(reverseString.value, amazingString.value, 3);
log(combineString.value, 4);
